package common;

import interfaces.Model;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import dao.FileIO;
import decision_tree.DecisionTreeModel;
import decision_tree.pos.POSManager;

public class Validater {
	private Map<String, Integer> map; 
	
	public double getPerplexity(String validationFile, String modelFile) {
		Model model = new DecisionTreeModel();
		model.read(modelFile);
		
		List<String> labelList = getLables(validationFile);
		List<List<Double>> distributionList = getDistribution(validationFile, model);
		
		return getPerplexity(distributionList, labelList);
	}
	
	public double getAccuracy(String validationFile, String modelFile) {
		Model model = new DecisionTreeModel();
		model.read(modelFile);
		
		List<String> labelList = getLables(validationFile);
		List<List<Double>> distributionList = getDistribution(validationFile, model);
		
		return getAccuracy(distributionList, labelList);
	}
	
	public List<String> getLables(String filename) {
		return FileIO.readFile(filename);
	}
	
	public List<List<Double>> getDistribution(String filename, Model model) {
		BatchedInputWrapper iw = new BatchedInputWrapper();
		iw.initialize(filename);
		return interaction(model, iw);
	}
	
	protected List<List<Double>> interaction(Model model, InputWrapper input) {
		List<String> history = new LinkedList<String>();
		List<List<Double>> result = new LinkedList<List<Double>>();
		while (true) {
			String line = input.next();

			if (line == null || line.isEmpty() || line.compareTo(TestController.EXIT) == 0) {
				break;
			}
			
			List<Double> distribution = model.test(history);
			result.add(distribution);

			history.add(line);
		}
		return result;
	}
	
	public double getPerplexity(List<List<Double>> distibutionList,
			List<String> labelList) {
		
		double infoSum = 0;
		Iterator<List<Double>> dItr = distibutionList.iterator();
		Iterator<Integer> lItr = getIndice(labelList).iterator();
		
		while(dItr.hasNext()) {
			double p = dItr.next().get(lItr.next());
			infoSum += - Math.log(p);
		}
		
		double entropy = infoSum / (distibutionList.size() * Math.log(2.0));
		double perplexity = Math.pow(2, entropy);
		
		return perplexity;
	}

	public double getAccuracy(List<List<Double>> distibutionList,
			List<String> labelList) {
		
		Iterator<Integer> eItr = getExpected(distibutionList).iterator();
		Iterator<Integer> lItr = getIndice(labelList).iterator();
		
		int hit = 0;
		while(eItr.hasNext()) {
			if(eItr.next() == lItr.next()) {
				hit ++;
			}
		}
		
		double accuracy = hit / (double) labelList.size();
		
		return accuracy;
	}
	
	protected List<Integer> getExpected (List<List<Double>> distibutionList) {
		List<Integer> expectedList = new ArrayList<Integer>(distibutionList.size());
		
		Iterator<List<Double>> dItr = distibutionList.iterator();
		while(dItr.hasNext()) {
			List<Double> list = dItr.next();
			
			int bestLabel = -1;
			double bestScore = -1;
			for(int i = 0; i < list.size(); i++) {
				if(list.get(i) > bestScore) {
					bestLabel = i;
					bestScore = list.get(i);
				}
			}

			expectedList.add(bestLabel);
		}
		return expectedList;
	}

	protected List<Integer> getIndice(List<String> labelList) {
		
		if(map == null) {
			map = new HashMap<String, Integer>();
			List<String> vocabulary = POSManager.getInstance().getVocabulary();
			
			for(int i = 0; i < vocabulary.size(); i++) {
				map.put(vocabulary.get(i), i);
			}
		}
		
		List<Integer> result = new ArrayList<Integer>(labelList.size());
		for(String key : labelList) {
			result.add(map.get(key));
		}

		return result;
	}
}
